Skip to content

M6: Native AOT validation and benchmarks (closes Phase 2)#6

Merged
lgamorim merged 6 commits into
masterfrom
feature/M6-aot-benchmarks
Jul 11, 2026
Merged

M6: Native AOT validation and benchmarks (closes Phase 2)#6
lgamorim merged 6 commits into
masterfrom
feature/M6-aot-benchmarks

Conversation

@lgamorim

Copy link
Copy Markdown
Owner

Summary

Delivers M6 and closes Phase 2 (Codegen) at a clean 0.2.0: no new query features — this milestone proves the two claims the phase has been making.

  • AOT/trim compatibility, validated two ways. IsAotCompatible on the SqlBound runtime package turns on the trim/single-file/AOT analyzers (zero findings; with TreatWarningsAsErrors any regression fails the build) and stamps the package metadata. A new aot-smoke-test CI job publishes test/SqlBound.AotSmokeTest with PublishAot and executes the native binary — it exercises every generated shape (list, single, optional, scalar, scalar list, streaming, execute, property-mapped rows) plus SqlSession against real SQLite, exit code as verdict.
  • Benchmarks. bench/SqlBound.Benchmarks (BenchmarkDotNet, convention recorded in architecture.md) compares SqlBound vs Dapper vs hand-written raw ADO.NET on identical SQL over in-memory SQLite across four categories, with MemoryDiagnoser. CI builds the project but never runs it; the baseline lives in docs/benchmarks.md from a real local run:
    • SqlBound within 1–9% of hand-written ADO.NET on point operations (scalar: 1.01×).
    • Allocation-identical to raw on 1,000-row list materialization (1.00×; Dapper 1.43×).
    • Ahead of Dapper in every category on both time and allocations.
    • The ~20% list-time gap vs raw is the generated per-column NULL guards (descriptive errors on unexpected NULLs) — a deliberate, documented trade.
  • Build-system notes: a bench-local Directory.Build.props shields BenchmarkDotNet''s generated host projects from the repo''s multi-targeting and warning escalation; benchmark method names share the tests'' async-suffix exemption since they label the result tables. The benchmark row type uses SQLite''s natural provider types (long/double) because Dapper''s constructor mapping requires exact type matches — documented as a fairness decision.
  • Phase close-out: the final commit drops PackageVersion to 0.2.0, so the v0.2.0 tag on the merge commit matches the package version exactly.

Test plan

  • 93 tests green in Release; dotnet format --verify-no-changes; pack verified at 0.2.0.
  • The new aot-smoke-test CI job gets its first real exercise on this PR — the native publish + run is the check to watch.

🤖 Generated with Claude Code

lgamorim and others added 5 commits July 11, 2026 17:56
IsAotCompatible turns on the trim, single-file, and AOT analyzers,
which report zero findings; with TreatWarningsAsErrors on, any future
regression fails the build. The flag also stamps the package metadata
consumers'' tooling reads when they publish trimmed or Native AOT apps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Trim analysis alone cannot prove AOT compatibility, so CI now publishes
a console app with PublishAot and executes the native binary: it
exercises every generated shape (list, single, optional, scalar, scalar
list, streaming, execute, property-mapped rows) plus SqlSession against
real SQLite, with the exit code as the verdict. PublishAot in the
project file also keeps the AOT analyzers on for every solution build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four categories (1000-row list, single row, scalar, execute) run the
identical SQL over in-memory SQLite three ways: hand-written ADO.NET as
the baseline ceiling, SqlBound generated code, and Dapper, with
MemoryDiagnoser since allocations are where reflection-free codegen
should show. Benchmarks live under bench/ (convention recorded in
architecture.md), join the solution so CI builds them, and are never
run in CI. A bench-local Directory.Build.props shields BenchmarkDotNet
generated host projects from the repo root defaults, which would
otherwise break them with multi-targeting and warning escalation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A full local run backs the docs: SqlBound stays within 1-9% of
hand-written ADO.NET on point operations, allocates exactly what the
hand-written loop allocates on 1000-row lists, and beats Dapper in
every category on both time and allocations. The row type switches to
SQLite''s natural provider types (long/double) because Dapper''s
constructor mapping requires exact type matches and crashed on
narrowing - noted in the docs as a fairness decision so all three
contenders do identical work. The generated null guards cost 20% on
list time versus raw; documented as the deliberate trade for
descriptive NULL errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dropping the prerelease suffix in the milestone''s final commit keeps
the v0.2.0 tag and the package version it marks identical, per the
versioning convention. Phase 3 (Verification) opens the 0.3.0-preview
line with its first commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lgamorim lgamorim modified the milestones: M6 — AOT + benchmarks, Phase 2 — Codegen (0.2.x) Jul 11, 2026
Restructured GitHub milestones so each phase gets one milestone
(matching its minor version) instead of one per M-number, keeping
all of a phase's PRs under a single milestone and making the
milestone description a compounded summary of its M-numbers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lgamorim lgamorim merged commit 10eb7ab into master Jul 11, 2026
4 checks passed
@lgamorim lgamorim deleted the feature/M6-aot-benchmarks branch July 11, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant